﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SSS.AlphaNN.Logic
{
    public static class ForGA
    {

        /// <summary>
        /// Генератор случайных чисел для всех
        /// </summary>
        public static Random rnd=new Random();


        /// <summary>
        /// Запуск генетического алгоритма
        /// </summary>
        /// <param name="input">Начальное приближение</param>
        /// <param name="funcOptimum">Функиця нахождения оптимума</param>
        /// <returns>оптимальное решение</returns>
        public static List<double> GARun(List<double> input, Func<List<double>, double> funcOptimum)
        {
            GenericGeneticAlg<double> gga = new GenericGeneticAlg<double>();
            gga._FunctionForOptimum = funcOptimum;
            gga._Optimum = x =>
            {
                double min = x.Min();
                return x.IndexOf(min);
            };
            gga._steps = 7;
            gga._NumberUsingMutation = 2;
            gga._FirstStep = () => input;
            gga._NumberFirstApproximation = 2;
            gga._Mutation.Add(y => y.Select(x =>
            {
                switch (rnd.Next(7))
                {
                    case 0:
                        return x + ((rnd.NextDouble() + 0.0001));
                    case 1:
                        return x - ((rnd.NextDouble() + 0.0001));
                    case 2:
                        return x * (rnd.Next(3) + 1) * (rnd.NextDouble() + 0.0001) / 5;
                    case 3:
                        return x * (rnd.Next(2) + 1) * (rnd.NextDouble() + 0.0001) / 5;
                    case 4:
                        return x * (rnd.NextDouble() + 0.0001);
                    case 5:
                        return x - rnd.NextDouble();
                    case 6:case 7:
                        double res = x;
                        for (int i = 0; i < rnd.Next(4);i++ )
                        {
                            x -= rnd.NextDouble();
                        }
                            return x - rnd.NextDouble() - rnd.NextDouble();
                    default:
                        return rnd.NextDouble();
                }

            }).ToList());

            gga._Mutation.Add(y => y.Select(x => rnd.NextDouble()
            ).ToList());


        
            gga._Mutation.Add(y => y.Select(x => x/2).ToList());

            gga._Mutation.Add(y =>
                                  {
                                      if (gga._FunctionForOptimum(y)>0)
                                      {
                                          return y.Select(c => c-rnd.NextDouble()).ToList();
                                      }
                                      else
                                      {
                                          return y.Select(c => c + rnd.NextDouble()).ToList();
                                      }
                                  });
            if (input.Count<2)
            {
                

                gga._Crossingover.Add((x, y) =>
                                              new List<double>() {(x[0] + y[0])/2}
                                          );
                gga._Crossingover.Add((x, y) =>
                                             new List<double>() {Math.Sqrt((x[0]*x[0] + y[0]*y[0])/4)  }
                                         );
                gga._Crossingover.Add((x, y) =>
                                             new List<double>() {GetP(x[0])  + Math.Round(y[0] )});

                gga._Crossingover.Add((x, y) =>
                                             new List<double>() { Math.Round(x[0]) + GetP(y[0]) });

                gga._CountArgs = 1;

                


                return gga.GeneticAlgorithm();
                
            }
            else
            {
                gga._Crossingover.Add((x, y) =>
                                          {
                                              List<double>
                                                  gens = new List<double>();

                                              gens.AddRange(x);
                                              gens.AddRange(y);

                                              List<double> result = new List<double>();
                                             

                                              while (result.Count < x.Count)
                                              {
                                                  result.Add(gens[rnd.Next(gens.Count - 1)]);
                                              }

                                              return result;
                                          });

                gga._Crossingover.Add((x, y) =>
                                          {
                                              int n = x.Count;
                                              List<double> result = new List<double>();
                                              if (n % 2 == 0)
                                              {
                                                  result.AddRange(x.GetRange(0, n / 2));
                                                  result.AddRange(y.GetRange(n / 2, n / 2));
                                              }
                                              else
                                              {
                                                  result.AddRange(x.GetRange(0, n / 2));
                                                  result.Add(SelectFromTwoNumbers(x[n / 2 + 1], y[n / 2 + 1]));
                                                  result.AddRange(x.GetRange(n / 2 + 1, n / 2));
                                              }

                                              return result;
                                          });

                 //gga._Crossingover.Add((x, y)=>
                 //                          {
                 //                              List<double> res = new List<double>();
                 //                              Random rnd = new Random();
                 //                              int countGen = x.Count;
                 //                              bool b = gga._FunctionForOptimum(x) < gga._FunctionForOptimum(y);
                 //                              double w = (b) ? 0.7D : 03D;

                 //                              while (res.Count < countGen)
                 //                              {
                 //                                  int n = rnd.Next(countGen - 1);

                 //                                  res.Add((w * x[n] + (1 - w) * y[n]));
                 //                              }

                 //                              return res;
                 //                          });
                return gga.GeneticAlgorithm();
            }

        }

        /// <summary>
        /// Взятие целой части от вещественного числа
        /// </summary>
        /// <param name="a"></param>
        /// <returns></returns>
        private static double GetP(double a)
        {
            return a - Math.Floor(a);
        }

        /// <summary>
        /// Выбор из двух чисел одного
        /// </summary>
        /// <param name="sX"></param>
        /// <param name="eY"></param>
        /// <returns></returns>
        private static double SelectFromTwoNumbers(double sX, double eY)
        {
            Random rnd = new Random();

            switch (rnd.Next(2))
            {
                case 1:
                    return sX;
                case 2:
                    return eY;
                case 0:
                    return (sX + eY) / 2;
                default:
                    return 0;
            }

            return 0;
        }
    }
}
